use chrono::NaiveDateTime;
use sea_orm::ActiveValue::NotSet;
use sea_orm::{
    ActiveModelTrait, ColumnTrait, ConnectionTrait, DbConn, DbErr, DerivePartialModel, EntityTrait,
    FromQueryResult, ModelTrait, PaginatorTrait, PartialModelTrait, QueryFilter, QueryOrder,
    QueryTrait, Set,
};
use serde::{Deserialize, Serialize};
use validator::Validate;

use crate::entity::sys_dict::Entity as SysDictType;
use crate::entity::sys_dict_data::Entity as SysDictData;
use crate::entity::{sys_dict, sys_dict_data};

pub struct SysDict;

#[derive(Debug, DerivePartialModel, FromQueryResult, Serialize)]
#[sea_orm(entity = "SysDictType")]
pub struct SysDictVo {
    pub id: i32,
    pub dict_name: String,
    pub dict_type: String,
    pub status: bool,
    pub remark: String,
    pub created_at: NaiveDateTime,
}

impl From<sys_dict::Model> for SysDictVo {
    fn from(value: sys_dict::Model) -> Self {
        Self {
            id: value.id,
            dict_name: value.dict_name,
            dict_type: value.dict_type,
            status: value.status,
            remark: value.remark,
            created_at: value.created_at,
        }
    }
}

/// 创建和修改[sys_dict_type]提交的数据
#[derive(Debug, Deserialize, Validate)]
pub struct SysDictDo {
    #[validate(length(min = 3, max = 32))]
    pub dict_name: String,
    #[validate(length(min = 3, max = 32), non_control_character)]
    pub dict_type: String,
    #[serde(default)]
    pub status: bool,
    #[validate(length(max = 255))]
    #[serde(default)]
    pub remark: String,
}

impl Into<sys_dict::ActiveModel> for SysDictDo {
    fn into(self) -> sys_dict::ActiveModel {
        sys_dict::ActiveModel {
            dict_name: Set(self.dict_name),
            dict_type: Set(self.dict_type),
            status: Set(self.status),
            remark: Set(self.remark),
            ..Default::default()
        }
    }
}

#[derive(Debug, DerivePartialModel, FromQueryResult, Serialize)]
#[sea_orm(entity = "SysDictData")]
pub struct SysDictDataVo {
    pub id: i32,
    pub dict_id: i32,
    pub dict_code: String,
    pub dict_value: String,
    pub dict_sort: i8,
    pub status: bool,
    pub created_at: NaiveDateTime,
}

#[derive(Debug, DerivePartialModel, FromQueryResult, Serialize)]
#[sea_orm(entity = "SysDictData")]
pub struct SysDictDataSimpleVo {
    pub dict_code: String,
    pub dict_value: String,
}

impl From<sys_dict_data::Model> for SysDictDataVo {
    fn from(value: sys_dict_data::Model) -> Self {
        Self {
            id: value.id,
            dict_id: value.dict_id,
            dict_code: value.dict_code,
            dict_value: value.dict_value,
            dict_sort: value.dict_sort,
            status: value.status,
            created_at: value.created_at,
        }
    }
}

/// 创建和修改[sys_dict_data]提交的数据
#[derive(Debug, Deserialize, Validate)]
pub struct SysDictDataDo {
    #[validate(length(min = 1, max = 32), non_control_character)]
    pub dict_code: String,
    #[validate(length(min = 1, max = 32))]
    pub dict_value: String,
    pub dict_sort: Option<i8>,
    #[serde(default)]
    pub status: Option<bool>,
}

// impl Into<sys_dict_data::ActiveModel> for SysDictDataDo {
//     fn into(self) -> sys_dict_data::ActiveModel {
//         sys_dict_data::ActiveModel {
//             dict_code: Set(self.dict_code),
//             dict_type: Set(self.dict_type),
//             dict_value: Set(self.dict_value),
//             dict_sort: Set(self.dict_sort),
//             status: Set(self.status),
//             ..Default::default()
//         }
//     }
// }

/// 字典的操作
#[allow(unused)]
impl SysDict {
    ane_macros::model_list!(
        #model = sys_dict
        #filter = []
        /// 分页列出[sys_dict]
        /// # params
        /// - `dict_name:Option<String>` 字典名称
        /// - `dict_type:Option<String>`  字典key
        /// - `status:Option<bool>`  true启用，false停用
        pub fn list_dict(DictName.contains(dict_name:Option<String>),DictType.contains(dict_type:Option<String>), Status.eq(status:Option<bool>),)
    );

    ane_macros::model_fetch!(
        #model=sys_dict
        /// 通过type获取[sys_dict]
        pub fn get_dict_by_type(DictType.eq(data_type:&str), Status.eq(status:Option<bool>))
    );

    /// 创建[sys_dict]
    pub async fn create_dict(
        db: &impl ConnectionTrait,
        data: SysDictDo,
        operator: i32,
    ) -> Result<(), DbErr> {
        let mut data: sys_dict::ActiveModel = data.into();
        data.created_by = Set(operator);
        data.updated_by = Set(operator);
        data.insert(db).await.map(|_| ())
    }

    /// 更新[sys_dict], 当id不存在时, 抛出错误
    pub async fn update_dict(
        db: &impl ConnectionTrait,
        id: i32,
        data: SysDictDo,
        operator: i32,
    ) -> Result<sys_dict::Model, DbErr> {
        let mut data: sys_dict::ActiveModel = data.into();
        data.id = Set(id);
        data.updated_by = Set(operator);
        data.update(db).await
    }

    /// 删除[sys_dict]
    pub async fn delete_dcit(db: &impl ConnectionTrait, id: i32) -> Result<bool, DbErr> {
        let model = sys_dict::Entity::find_by_id(id).one(db).await?;
        if let Some(model) = model {
            sys_dict_data::Entity::delete_many()
                .filter(sys_dict_data::Column::DictId.eq(model.id))
                .exec(db)
                .await?;
            model.delete(db).await.map(|r| r.rows_affected > 0)
        } else {
            Ok(true)
        }
    }
}

/// 字典数据的操作
#[allow(unused)]
impl SysDict {
    ane_macros::model_list! {
        #model=sys_dict_data
        #order=[DictSort:Asc]
        /// 根据字典id列出字典数据[sys_dict_data]
        pub fn list_dict_data_by_id(DictId.eq(dict_id:i32), Status.eq(status:Option<bool>))
    }

    /// 根据字典type列出**全部**字典数据[sys_dict_data]，该字典的status需要是true
    pub async fn list_dict_data_by_type<T>(
        db: &impl ConnectionTrait,
        dict_type: String,
        status: Option<bool>,
    ) -> Result<Vec<T>, DbErr>
    where
        T: PartialModelTrait + Send + Sync,
    {
        sys_dict_data::Entity::find()
            .inner_join(sys_dict::Entity)
            .filter(sys_dict::Column::DictType.eq(dict_type))
            .filter(sys_dict::Column::Status.eq(true))
            .apply_if(status, |sql, v| {
                sql.filter(sys_dict_data::Column::Status.eq(v))
            })
            .order_by_asc(sys_dict_data::Column::DictSort)
            .into_partial_model()
            .all(db)
            .await
    }

    /// 创建字典的数据[sys_dict_data]
    pub async fn create_dict_data<D>(
        db: &DbConn,
        dict_id: i32,
        data: SysDictDataDo,
        operator: i32,
    ) -> Result<sys_dict_data::Model, DbErr> {
        let mut data: sys_dict_data::ActiveModel = sys_dict_data::ActiveModel {
            dict_id: Set(dict_id),
            dict_code: Set(data.dict_code),
            dict_value: Set(data.dict_value),
            dict_sort: Set(data.dict_sort.unwrap_or(1)),
            status: Set(data.status.unwrap_or(true)),
            created_by: Set(operator),
            updated_by: Set(operator),
            ..Default::default()
        };
        data.insert(db).await
    }

    /// 更新[sys_dict_data], 当id不存在时, 抛出错误
    pub async fn update_dict_data<D>(
        db: &DbConn,
        dict_id: i32,
        id: i32,
        data: SysDictDataDo,
        operator: i32,
    ) -> Result<sys_dict_data::Model, DbErr> {
        let mut data: sys_dict_data::ActiveModel = sys_dict_data::ActiveModel {
            id: Set(id),
            dict_code: Set(data.dict_code),
            dict_value: Set(data.dict_value),
            dict_sort: data.dict_sort.map(Set).unwrap_or(NotSet),
            status: data.status.map(Set).unwrap_or(NotSet),
            updated_by: Set(operator),
            ..Default::default()
        };
        sys_dict_data::Entity::update(data)
            .filter(sys_dict_data::Column::DictId.eq(dict_id))
            .exec(db)
            .await
    }

    /// 删除[sys_dict_data], 当id不存在时, 抛出错误
    pub async fn delete_dict_data(db: &DbConn, id: i32) -> Result<bool, DbErr> {
        sys_dict_data::ActiveModel {
            id: Set(id),
            ..Default::default()
        }
        .delete(db)
        .await
        .map(|r| r.rows_affected > 0)
    }
}
